#ifndef __M_QUEUE_H__
#define __M_QUEUE_H__

#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <google/protobuf/map.h>
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <string>

namespace wsmq
{
    struct MsgQueue
    {
        using ptr = std::shared_ptr<MsgQueue>;
        std::string _name;
        bool _durable;
        bool _exclusive; // 是否独占标识
        bool _auto_delete;
        // std::unordered_map<std::string, std::string> _args;
        google::protobuf::Map<std::string, std::string> _args;
        MsgQueue() {}
        MsgQueue(std::string name,
                 bool durable, bool exclusive, bool auto_delete,
                 const google::protobuf::Map<std::string, std::string>& args) : _name(name),
                                                                      _durable(durable), _exclusive(exclusive),
                                                                      _auto_delete(auto_delete),
                                                                      _args(args)
        {
        }
        void setArgs(const std::string &str_args)
        {
            // key=val&key=val&
            std::vector<std::string> sub_args;
            StrHelper::split(str_args, "&", sub_args);
            for (auto &str : sub_args)
            {
                size_t pos = str.find("=");
                std::string key = str.substr(0, pos);
                std::string val = str.substr(pos + 1);
                _args[key] = val;
            }
        }
        // 将args中的内容进行序列化后，返回一个字符串
        std::string getArgs()
        {
            std::string result;
            for (auto start = _args.begin(); start != _args.end(); ++start)
            {
                result += start->first + "=" + start->second + "&";
            }
            return result;
        }
    };
    using MsgQueueMap = std::unordered_map<std::string, MsgQueue::ptr>;
    class MsgQueueMapper
    {
    public:
        using ptr = std::shared_ptr<MsgQueueMapper>;
        MsgQueueMapper(const std::string &dbfile) : _sql_helper(dbfile)
        {
            // 持久化类初始化时，根据数据库文件路径名创建目录，并打开数据库、建queue_table
            std::string path = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(path);
            assert(_sql_helper.open());
            createTable();
        }
        void createTable()
        {
            std::stringstream sql;
                sql << "create table if not exists queue_table(";
                sql << "name varchar(32) primary key, ";
                sql << "durable int, ";
                sql << "exclusive int, ";
                sql << "auto_delete int, ";
                sql << "args varchar(128));";
                assert(_sql_helper.exec(sql.str(), nullptr, nullptr));
        }
        void removeTable()
        {
            std::string sql = "drop table if exists queue_table;";
            _sql_helper.exec(sql, nullptr, nullptr);
        }
        bool insert(MsgQueue::ptr &queue)
        {
            std::stringstream sql;
                sql << "insert into queue_table values(";
                sql << "'" << queue->_name << "', ";
                sql << queue->_durable << ", ";
                sql << queue->_exclusive << ", ";
                sql << queue->_auto_delete << ", ";
                sql << "'" << queue->getArgs() << "');";
                return _sql_helper.exec(sql.str(), nullptr, nullptr);
        }
        void remove(const std::string &name)
        {
            std::stringstream sql;
            sql << "delete from queue_table where name=";
            sql << "'" << name << "';";
            _sql_helper.exec(sql.str(), nullptr, nullptr);
        }
        MsgQueueMap recovery()
        {
            MsgQueueMap result;
            std::string sql = "select name, durable, exclusive, auto_delete, args from queue_table;";
            _sql_helper.exec(sql, selectCallBack, &result);
            return result;
        }

    private:
        static int selectCallBack(void *args, int numcol, char **row, char **feilds)
        {
            MsgQueueMap *result = (MsgQueueMap *)args;
            auto msp = std::make_shared<MsgQueue>();
            msp->_name = row[0];
            msp->_durable = (bool)std::stoi(row[1]);
            msp->_exclusive = (bool)std::stoi(row[2]);
            msp->_auto_delete = (bool)std::stoi(row[3]);
            if (row[4])
                msp->setArgs(row[4]);
            result->insert(std::make_pair(msp->_name, msp));
            return 0; // 这个回调函数必须返回0，返回非0则认为错误
        }

    private:
        SqliteHelper _sql_helper;
    };
    class MsgQueueManager
    {
    public:
        using ptr = std::shared_ptr<MsgQueueManager>;
        MsgQueueManager(const std::string &dbfile) : _mapper(dbfile)
        {
            _queues = _mapper.recovery(); // 将持久化的数据恢复到内存中（如果有的话）
        }
        bool declareQueue(const std::string &name,
                          bool durable, bool exclusive, bool auto_delete,
                          const google::protobuf::Map<std::string, std::string>& args)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _queues.find(name);
            if (it != _queues.end()) // 如果存在，就不用新增了
            {
                return true;
            }
            MsgQueue::ptr mqp = std::make_shared<MsgQueue>();
            mqp->_name = name;
            mqp->_durable = durable;
            mqp->_exclusive = exclusive;
            mqp->_auto_delete = auto_delete;
            mqp->_args = args;
            if (durable == true) {
                bool ret = _mapper.insert(mqp);
                if (ret == false) return false;
            }
            _queues.insert(std::make_pair(name, mqp));
            return true;
        }
        void removeQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _queues.find(name);
            if (it == _queues.end())
            {
                return;
            }
            if (it->second->_durable == true)
            {
                _mapper.remove(name);
            }
            _queues.erase(name);
        }
        bool exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _queues.find(name);
            if (it == _queues.end())
            {
                return false;
            }
            return true;
        }
        MsgQueueMap allQueues()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _queues;
        }

        MsgQueue::ptr selectMsgQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _queues.find(name);
            if (it == _queues.end())
            {
                return MsgQueue::ptr();
            }
            return it->second;
        }
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _queues.size();
        }
        void clear()
        {
            _mapper.removeTable();
            _queues.clear();
        }

    private:
        std::mutex _mutex;
        MsgQueueMapper _mapper;
        MsgQueueMap _queues;
    };
}

#endif